package top.ninwoo.edgecenter.controller;

import org.influxdb.InfluxDB;
import org.influxdb.dto.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import top.ninwoo.common.entity.ClusterConfig;
import top.ninwoo.common.entity.DockerContainer;
import top.ninwoo.common.entity.NetworkTopology;
import top.ninwoo.edgecenter.entity.ContainerInfo;
import top.ninwoo.edgecenter.service.ClusterService;
import top.ninwoo.edgecenter.service.IpService;
import top.ninwoo.edgecenter.service.TopologyService;
import top.ninwoo.edgecenter.service.LogicTopoService;
import top.ninwoo.utils.entity.NetworkInfo;
import top.ninwoo.utils.entity.OvsBridge;
import top.ninwoo.utils.service.TcService;

import java.util.*;
import java.util.concurrent.*;

@RestController
public class IndexController {
    ConcurrentHashMap<String, ContainerInfo> containerInfoMap = new ConcurrentHashMap<>();
    ConcurrentHashMap<String, Future> scheduledFutureMap = new ConcurrentHashMap<>();
    ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(8);

    @Autowired
    private ClusterService clusterService;

    @Autowired
    private TopologyService topologyService;

    @Autowired
    private LogicTopoService logicTopoService;

    @Autowired
    private IpService ipService;

    @Autowired
    private InfluxDB influxDB;

    @Autowired
    private TcService tcService;

    @RequestMapping(value = "/index")
    public List<DockerContainer> index(int flag) {

        List<DockerContainer> containerIds = clusterService.getContainerIds(flag == 0);
        return containerIds;
    }

    @RequestMapping(value = "/addQos")
    public Boolean addQos(Long clusterId, String cName, String maxRate, String latency) {
        Set<String> cids = clusterService.getContainerIdsByClusterId(clusterId, cName);
        boolean res = tcService.addQos(cids.toArray(new String[]{})[0], maxRate, latency);
        return res;
    }

    @RequestMapping("/ovs")
    public List<OvsBridge>  getOvsBridgeList() {
        List<OvsBridge> bridges = clusterService.getOvsBridges();
        return bridges;
    }

    /**
     * 查询容器的网络信息，根据容器的id，获取对应集群的全部节点网络实时情况
     * @param clusterId
     * @param containerId
     * @return
     */
    // 这里一次查询需要两秒，需要采用异步进行查询
    // TODO： 这里的containerId指的是容器名称
    @RequestMapping("/containerMonitor")
    public List<ContainerInfo> getContainerMonitor(@RequestParam("clusterId") Long clusterId,@RequestParam("containerId") String containerId) {
        // 获取全部的容器id
        Set<String> cids = clusterService.getContainerIdsByClusterId(clusterId, containerId);

        // 根据容器id获取全部的
        //List<NetworkInfo> networkTraffic = clusterService.getNetworkTraffic(containerIdsByClusterId);
        List<ContainerInfo> containerInfos = new ArrayList<>();
        cids.forEach( cid -> {
            if(containerInfoMap.containsKey(cid)) {
                ContainerInfo networkInfo = containerInfoMap.get(cid);
                containerInfos.add(networkInfo);
            }
            // TODO: 这里还可以再添加新的处理方式
        });
        return containerInfos;
    }

    @RequestMapping("/getContainerMonitorByContainerId")
    public ContainerInfo getContainerMonitorByContainerId(String containerId) {
        if(containerInfoMap.containsKey(containerId)) {
            return containerInfoMap.get(containerId);
        }
        return null;
    }

    @RequestMapping("/enableContainerMonitor")
    public boolean enableNetworkMonitor(long clusterId, String containerName) {
        // 先获取全部的容器id
        Set<String> cids = clusterService.getContainerIdsByClusterId(clusterId, containerName);
        if(cids == null || cids.isEmpty()) {
            return false;
        }
        cids.forEach(cid -> {
            // 为每一个cid容器创建一个定时任务
            if (!scheduledFutureMap.containsKey(cid)) {
                containerInfoMap.put(cid, new ContainerInfo());
                NetworkMonitorThread networkMonitorThread = new NetworkMonitorThread(cid, containerInfoMap, clusterService, influxDB, containerName);
                // 放入线程池执行任务
                ScheduledFuture<?> future = scheduledExecutorService.scheduleAtFixedRate(networkMonitorThread, 0, 10 , TimeUnit.MICROSECONDS);
                // 保存任务，以方便进行定时任务的关闭
                scheduledFutureMap.put(cid, future);
            }
        });

        return true;
    }

    @RequestMapping("/cancelNetworkMonitor")
    public boolean cancelNetworkMonitor(long clusterId, String containerName) {
        // 先获取全部的容器id
        Set<String> cids = clusterService.getContainerIdsByClusterId(clusterId, containerName);
        if(cids == null || cids.isEmpty()) {
            return false;
        }

        cids.forEach(cid -> {
            // TODO: 不安全的操作
            //  cancel不安全，get(cid)不安全
            if(scheduledFutureMap.containsKey(cid)) {
                scheduledFutureMap.get(cid).cancel(false);
                while (!scheduledFutureMap.get(cid).isCancelled()) {
                    System.out.println("等待线程停止" + cid);
                }
                scheduledFutureMap.remove(cid);
                containerInfoMap.remove(cid);
            }
        });
        return true;
    }


    @RequestMapping(value = "/createCluster", method = RequestMethod.POST)
    public Map<String, Set<String>> createCluster(@RequestBody ClusterConfig clusterConfig) {
        // 创建一个解析器
        Map<String, Set<String>> resMap = clusterService.initContainers(clusterConfig);
        // 创建网络
        // 使用topo创建工具
        topologyService.createTopo(clusterConfig.getId(), clusterConfig.getTopology());
        return resMap;
    }

    @RequestMapping(value = "/getContainerIds")
    public Set<String> getContainerIds(long clusterId, String containerName) {
        return clusterService.getContainerIdsByClusterId(clusterId, containerName);
    }

    private static class NetworkMonitorThread implements Runnable {
        private String containerId;
        private ConcurrentHashMap<String, ContainerInfo> map;
        private ClusterService clusterService;
        private InfluxDB influxDB;
        private String cName;
        private static final Logger LOG = LoggerFactory.getLogger(NetworkMonitorThread.class);

        public NetworkMonitorThread(String containerId, ConcurrentHashMap<String, ContainerInfo> map, ClusterService clusterService, InfluxDB influxDB, String cName) {
            this.containerId = containerId;
            this.map = map;
            this.clusterService = clusterService;
            this.influxDB = influxDB;
            this.cName = cName;
        }

        @Override
        public void run() {
            NetworkInfo networkInfo = clusterService.getNetworkInfoByContainerId(containerId);
            String cpu = clusterService.cpu(containerId);
            String mem = clusterService.mem(containerId);
            ContainerInfo containerInfo = new ContainerInfo();
            containerInfo.setCpu(cpu);
            containerInfo.setId(containerId);
            containerInfo.setNetworkInfo(networkInfo);
            containerInfo.setMem(mem);
            if(map.containsKey(containerId)) {
                map.put(containerId, containerInfo); // TODO: 这里有又重新把容器ｉｄ注册回去了
            }
            try {
                savePoint(cName,networkInfo);
                LOG.info("数据监控中");
            } catch (Exception e) {
                LOG.error("监控错误 {}",e.getMessage());
            }

        }

        private void savePoint(String cName, NetworkInfo networkInfo) {
            if(networkInfo == null) {
                return;
            }
            if(networkInfo.getCurRate() > 800f) {
                return;
            }

            Point.Builder builder = Point.measurement(cName);
            Point point = builder
                    .addField("rate", networkInfo.getCurRate())
                    //.addField("error", networkInfo.getErrorRate())
                    //.addField("drop", networkInfo.getDropRate())
                    .tag("cid", containerId)
                    .tag("cName", cName).build();
            influxDB.setDatabase("test").write(point);
        }
        public void setContainerId(String containerId) {
            this.containerId = containerId;
        }
    }

    @RequestMapping("/edgecenter")
    public Map<String, String> edgeCenter() {
        HashMap<String, String> edgeInfo = new HashMap<>();
        edgeInfo.put("cpu", clusterService.cpu());
        edgeInfo.put("mem", clusterService.mem());
        return edgeInfo;
    }

    @RequestMapping(value = "/createTopo", method = RequestMethod.POST)
    public NetworkTopology createTopo(long clusterId, long topologyId, String[] appNames, String topology) {
        // 二维数组作为一个字符串传递进来，需要进行一个解析
        NetworkTopology topo = new NetworkTopology();
        topo.setAppNames(appNames);
        topo.setTopologyId(111);
        topo.setTopology(parseString(topology));
        // 使用topo创建工具
        topologyService.createTopo(clusterId, topo);
        return topo;
    }

    @RequestMapping(value = "/delCluster", method = RequestMethod.GET)
    public String delCluster(long clusterId) {
        // 删除集群
        topologyService.removeTopoByCluterId(clusterId);
        clusterService.removeContainersByClusterId(clusterId);

        return "success";
    }

    @RequestMapping(value = "/forwardTraffic")
    public boolean forwardTraffic(String forwardContainer, String targetContainer, int port) {
        return topologyService.forwardTraffic(forwardContainer, targetContainer, port);
    }

    private int[][] parseString(String topology) {
        String[] lines = topology.split(";");
        int x = lines.length;
        int y = lines[0].split(",").length;

        int[][] topo = new int[x][y];
        for (int i = 0; i < x; i++) {
            String[] integers = lines[i].split(",");
            for (int j = 0; j < y; j++) {
                topo[i][j] = Integer.parseInt(integers[j]);
            }
        }
        return topo;
    }

    @RequestMapping(value = "/adjustCluster", method = RequestMethod.POST)
    public String adjustCluster(@RequestBody ClusterConfig clusterConfig) {
        // 判断id
        if (clusterConfig.getId() == 0) {
            return "没有设置clusterid";
        }
        String res = clusterService.adjustCluster(clusterConfig);
        String res1 = topologyService.modifyTopology(clusterConfig.getId(), clusterConfig.getTopology());
        return res + "|" + res1;
    }

    @RequestMapping(value = "/adjustClusterTopo")
    public String adjustClusterTopo(long clusterId, long topologyId, String[] appNames, String topology) {
        // 创建集群id
        NetworkTopology topo = new NetworkTopology();
        topo.setAppNames(appNames);
        topo.setTopologyId(111);
        topo.setTopology(parseString(topology));
        String res = topologyService.modifyTopology(clusterId, topo);
        return res;
    }

    @RequestMapping(value = "/dropDockerNetwork")
    public String dropDockerNetwork(long clusterId, String appName, String[] ipList) {
        List<String> ips = Arrays.asList(ipList);
        topologyService.dropDockerNetwork(clusterId, appName, ips);
        return "success";
    }

    @RequestMapping(value = "/cancelDropDockerNetwork")
    public String cancelDropDockerNetwork(long clusterId, String appName, String[] ipList) {
        topologyService.cancelDropDockerNetwork(clusterId, appName, Arrays.asList(ipList));
        return "success";
    }

    @RequestMapping(value = "/getAppIpsByAppName")
    public Set<String> getAppIpsByAppName(long clusterId, String appName) {
        Set<String> ids = clusterService.getContainerIdsByClusterId(clusterId, appName);
        HashSet<String> ips = new HashSet<>();
        ids.forEach(id -> {
            ips.add(ipService.getContainerIp(id));
        });

        return ips;
    }
    //下发逻辑拓扑
    @RequestMapping(value = "/sendLogicTopo", method = RequestMethod.POST)
    public String sendLogicTopo(@RequestBody ClusterConfig clusterConfig) {
        if(clusterConfig == null){
            return "请传入正确的集群设置";
        }
        // 使用topo创建工具
        String res = logicTopoService.creatLogicTopo(clusterConfig.getId(), clusterConfig.getTopology());
        //System.out.println("ok");
        return res;
    }
    //修改逻辑拓扑
    @RequestMapping(value = "/adjustLogicTopo", method = RequestMethod.POST)
    public String adjustLogicTopo(@RequestBody ClusterConfig clusterConfig) {
        if(clusterConfig == null){
            return "请传入正确的集群设置";
        }
        // 判断id
        if (clusterConfig.getId() == 0) {
            return "没有设置clusterid";
        }
        String res1 = logicTopoService.modifyLogicTopology(clusterConfig.getId(), clusterConfig.getTopology());
        //System.out.println("ok?");
        return res1;
    }

    //
    @GetMapping("/getIPbyName")
    public String getIPbyname(@RequestParam(name = "appname")String appname,@RequestParam(name = "clusterId")long clusterId) {
        Set<String> cid = clusterService.getContainerIdsByClusterId(clusterId, appname);
        if (cid == null){
            System.out.println("错误的clusterID");
            return "获取失败";
        }
        String appID = ((String) cid.toArray()[0]);
        if(appID ==null){
            System.out.println("错误的appnmae");
            return "获取失败";
        }
        String appIP = ipService.getContainerIp(appID).split("/")[0];

        return appIP;
    }
}